package fast.calc;

import indzcraft.loader.Spec;

import java.util.ArrayList;
import java.util.List;

public class Stats {
	
	// lvl 85
	static double HASTE_CONVERSION_RATING = 128.057;
	static double HIT_CONVERSION_RATING = 102.4457;
	static double EXP_CONVERSION_RATING = 102.4457;
	static double MASTERY_CONVERSION_RATING = 179.28;
	static double CRIT_CONVERSION_RATING = 179.28;
	static double AGI_TO_CRIT_CONVERSION_RATING = 322.580657958984375;
	static double BOSS_ARMOR = 11977;
	
//	// FIXME LEVEL_90
//	
//	static double HASTE_CONVERSION_RATING = 425;
//	static double HIT_CONVERSION_RATING = 340;
//	static double EXP_CONVERSION_RATING = 340;
//	static double MASTERY_CONVERSION_RATING = 600;
//	static double CRIT_CONVERSION_RATING = 600;
//	static double AGI_TO_CRIT_CONVERSION_RATING = 1259.518066406250000;
//	static double BOSS_ARMOR = 24835;
	
	static double AGI_INTERCEPT = -0.003000000026077;
	
	private Race race;
	private Spec spec;
	private int str;
	private int agi;
	private int sta;
	private int hit;
	private int ap;
	private int crit;
	private int haste;
	private int exp;
	private int mastery;
	
	private WeaponStats mh;
	private WeaponStats oh;
	
	private Buffs buffs;
	private Procs procs = new Procs();
	private GearBuffs gearBuffs = new GearBuffs();
	private List<String> glyphs = new ArrayList<String>();
	
	public String calcEP = "";
	
	public void setStr(int str) {
		this.str = str;
	}
	public int getStr() {
		return str;
	}
	public double getStrBuffed(){
		double str = this.str;
		if(buffs.isAllStatsBuff())
			str *= 1.05;
		return str;
	}
	public void setAgi(int agi) {
		this.agi = agi;
	}
	public int getAgi() {
		return agi;
	}
	public double getAgiBuffed() {
		double agi = this.agi;
		agi += procs.agi;
		if(buffs.isAgiFlask())
			agi += 300; // FIXME LEVEL_90
		if(buffs.isAgiFood()){
			double food = 90; // FIXME LEVEL_90
			if(gearBuffs.has("epicurean"))
				food *= 2;
			agi += food;
		}
		if(spec == Spec.Subtlety)
			agi *= 1.3;
		if(buffs.isAllStatsBuff())
			agi *= 1.05;
		agi *= 1.05; // leather spec baseline
		return agi;
	}
	public void setSta(int sta) {
		this.sta = sta;
	}
	public int getSta() {
		return sta;
	}
	public void setAp(int ap) {
		this.ap = ap;
	}
	public int getAp() {
		return ap;
	}
	public double getApBuffed() {
		double ap = this.ap + getStrBuffed() + 2 * getAgiBuffed();
		ap += procs.ap;
		if(gearBuffs.has("blood_fury"))
			ap += 4514 * 15. / 120.;
		if(spec == Spec.Combat)
			ap *= 1.25;
		if(buffs.isAttackPowerBuff())
			ap *= 1.1;
		return ap;
	}
	public void setHit(int hit) {
		this.hit = hit;
	}
	public int getHit() {
		return hit;
	}
	public void setCrit(int crit) {
		this.crit = crit;
	}
	public int getCrit() {
		return crit;
	}
	public double getCritBuffed() {
		return crit+procs.crit;
	}
	public double getCritPercent(){
		double RATING_AGI_TO_CRIT = AGI_TO_CRIT_CONVERSION_RATING;
		double crit_reduction = -3.0;
		double agi_crit_depression = AGI_INTERCEPT;
		double crit_buffs = 0.0;
		if(buffs.isCritBuff())
			crit_buffs = 5.0;
		if(gearBuffs.has("viciousness"))
			crit_buffs += 1.0;
		double agi_crit = this.getAgiBuffed()/RATING_AGI_TO_CRIT+agi_crit_depression;
		return (this.getCritBuffed()/CRIT_CONVERSION_RATING + crit_buffs+crit_reduction+agi_crit)/100;
	}
	public void setHaste(int haste) {
		this.haste = haste;
	}
	public int getHaste() {
		return haste;
	}
	public double getHasteBuffed() {
		return haste+procs.haste;
	}
	public void setExp(int exp) {
		this.exp = exp;
	}
	public int getExp() {
		return exp;
	}
	public void setMastery(int mastery) {
		this.mastery = mastery;
	}
	public int getMastery() {
		return mastery;
	}
	public double getMasteryBuffed() {
		double mastery = 8 + (this.mastery + procs.mastery) / MASTERY_CONVERSION_RATING;
		if(buffs.isMasteryBuff())
			mastery += 5;
		return mastery;
	}
	public void setMh(WeaponStats mh) {
		this.mh = mh;
	}
	public WeaponStats getMh() {
		return mh;
	}
	public void setOh(WeaponStats oh) {
		this.oh = oh;
	}
	public WeaponStats getOh() {
		return oh;
	}
	public void setBuffs(Buffs buffs) {
		this.buffs = buffs;
	}
	public Buffs getBuffs() {
		return buffs;
	}
	public void setSpec(Spec spec) {
		this.spec = spec;
	}
	public Spec getSpec() {
		return spec;
	}
	public void setRace(Race race) {
		this.race = race;
	}
	public Race getRace() {
		return race;
	}
	
	public double getMhHitChance() {
		double base_miss = 7.5+19; // 19% dual wield penalty
		double base_dodge = 7.5;
		double hit_per = this.hit / HIT_CONVERSION_RATING;
		double dodge_per = this.exp / EXP_CONVERSION_RATING;
		
		if(mh.getType() == WeaponType.dagger && (race == Race.gnome))
			dodge_per += 1;
		if(mh.getType() == WeaponType.sword && (race == Race.human || race == Race.gnome))
			dodge_per += 1;
		if(mh.getType() == WeaponType.mace && (race == Race.human || race == Race.dwarf))
			dodge_per += 1;
		if(mh.getType() == WeaponType.axe && (race == Race.orc))
			dodge_per += 1;
				
		double misses = Math.max(base_miss - hit_per, 0);
		double dodges = Math.max(base_dodge - dodge_per, 0);
		
		if(calcEP.equals("exp_mh"))
			dodges += 1 / EXP_CONVERSION_RATING;
		if(calcEP.equals("style_hit") || calcEP.equals("white_hit"))
			misses += 1 / HIT_CONVERSION_RATING;
		
		return (100-misses-dodges)/100;
	}
	
	public double getOhHitChance() {
		double base_miss = 7.5+19; // 19% dual wield penalty
		double base_dodge = 7.5;
		double hit_per = this.hit / HIT_CONVERSION_RATING;
		double dodge_per = this.exp / EXP_CONVERSION_RATING;
		
		if(oh.getType() == WeaponType.dagger && (race == Race.gnome))
			dodge_per += 1;
		if(oh.getType() == WeaponType.sword && (race == Race.human || race == Race.gnome))
			dodge_per += 1;
		if(oh.getType() == WeaponType.mace && (race == Race.human || race == Race.dwarf))
			dodge_per += 1;
		if(oh.getType() == WeaponType.axe && (race == Race.orc))
			dodge_per += 1;
		
		double misses = Math.max(base_miss - hit_per, 0);
		double dodges = Math.max(base_dodge - dodge_per, 0);
		
		if(calcEP.equals("exp_oh"))
			dodges += 1 / EXP_CONVERSION_RATING;
		if(calcEP.equals("style_hit") || calcEP.equals("white_hit"))
			misses += 1 / HIT_CONVERSION_RATING;
		
		return (100-misses-dodges)/100;
	}
	
	public double getMhStyleHitChance() {
		double base_miss = 7.5;
		double base_dodge = 7.5;
		double hit_per = this.hit / HIT_CONVERSION_RATING;
		double dodge_per = this.exp / EXP_CONVERSION_RATING;
		
		if(mh.getType() == WeaponType.dagger && (race == Race.gnome))
			dodge_per += 1;
		if(mh.getType() == WeaponType.sword && (race == Race.human || race == Race.gnome))
			dodge_per += 1;
		if(mh.getType() == WeaponType.mace && (race == Race.human || race == Race.dwarf))
			dodge_per += 1;
		if(mh.getType() == WeaponType.axe && (race == Race.orc))
			dodge_per += 1;
		
		double misses = Math.max(base_miss - hit_per, 0);
		double dodges = Math.max(base_dodge - dodge_per, 0);
		
		if(calcEP.equals("exp_mh"))
			dodges += 1 / EXP_CONVERSION_RATING;
		if(calcEP.equals("style_hit"))
			misses += 1 / HIT_CONVERSION_RATING;
		
		return (100-misses-dodges)/100;
	}
	
	public double getOhStyleHitChance() {
		double base_miss = 7.5;
		double base_dodge = 7.5;
		double hit_per = this.hit / HIT_CONVERSION_RATING;
		double dodge_per = this.exp / EXP_CONVERSION_RATING;
		
		if(oh.getType() == WeaponType.dagger && (race == Race.gnome))
			dodge_per += 1;
		if(oh.getType() == WeaponType.sword && (race == Race.human || race == Race.gnome))
			dodge_per += 1;
		if(oh.getType() == WeaponType.mace && (race == Race.human || race == Race.dwarf))
			dodge_per += 1;
		if(oh.getType() == WeaponType.axe && (race == Race.orc))
			dodge_per += 1;
		
		double misses = Math.max(base_miss - hit_per, 0);
		double dodges = Math.max(base_dodge - dodge_per, 0);
		
		if(calcEP.equals("exp_oh"))
			dodges += 1 / EXP_CONVERSION_RATING;
		if(calcEP.equals("style_hit"))
			misses += 1 / HIT_CONVERSION_RATING;
		
		return (100-misses-dodges)/100;
	}
	
	public double getMhPoisonHitChance() {
		double base_miss = 7.5;
		double hit_per = this.hit / HIT_CONVERSION_RATING;
		
		double misses = Math.max(base_miss - hit_per, 0);
		
		if(calcEP.equals("style_hit"))
			misses += 1 / HIT_CONVERSION_RATING;
		
		return (100-misses)/100;
	}
	
	public double getOhPoisonHitChance() {
		double base_miss = 7.5;
		double hit_per = this.hit / HIT_CONVERSION_RATING;
			
		double misses = Math.max(base_miss - hit_per, 0);
		
		if(calcEP.equals("style_hit"))
			misses += 1 / HIT_CONVERSION_RATING;
		return (100-misses)/100;
	}
	
	public double getCritMultiplier() {
		double base = 2.0;
		if(gearBuffs.has("metagem_critdmg"))
			base *= 1.03;
		return base;
	}
	
	public Procs getProcs() {
		return procs;
	}
	public GearBuffs getGearBuffs() {
		return gearBuffs;
	}
	public void setGearBuffs(List<String> gbs) {
		gearBuffs.setGearBuffs(gbs);
	}
	public void setSetItems2(List<String> setItems2) {
		gearBuffs.setSetItems2(setItems2);		
	}
	public List<String> getGlyphs() {
		return glyphs;
	}
	public void setGlyphs(List<String> glyphs) {
		this.glyphs = glyphs;
	}
	public boolean hasGlyph(String name) {
		return this.glyphs.contains(name);
	}
}
